home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / pluginy Firefox / 14642 / 14642.xpi / chrome / modules / app.js next >
Text File  |  2009-10-10  |  23KB  |  636 lines

  1. /* Copyright 2009, Boomtango.com.  All Rights Reserved. */
  2. /* app.js
  3.  * Responsible for high level management of components
  4.  */
  5. var EXPORTED_SYMBOLS = ["boomtangoApp"];
  6.  
  7. Components.utils.import("resource://boomtango/tracker.js");
  8. Components.utils.import("resource://boomtango/storage.js");
  9.  
  10. var boomtangoApp = {
  11.     cc: Components.classes,
  12.     ci: Components.interfaces,
  13.     _log:null,
  14.     newUser: false,
  15.     init: function() {
  16.         this.tracker = boomtangoTracker;
  17.         this.storage = boomtangoStorage;
  18.         this._nextID = 1;
  19.         this.sessionStart = Date.now();
  20.         
  21.         // create the boomtango directory if it doesn't exist
  22.         var dir = Components.classes["@mozilla.org/file/directory_service;1"]
  23.                              .getService(Components.interfaces.nsIProperties)
  24.                              .get("ProfD", Components.interfaces.nsIFile);
  25.         dir.append("boomtango");
  26.         if(!dir.exists() || !dir.isDirectory()){
  27.             dir.create(dir.DIRECTORY_TYPE, 0744);
  28.             var datadir = dir.clone();
  29.             datadir.append("data");
  30.             datadir.create(dir.DIRECTORY_TYPE, 0744);
  31.             
  32.             var trackerdir = dir.clone();
  33.             trackerdir.append("trackers");
  34.             trackerdir.create(trackerdir.DIRECTORY_TYPE, 0744);
  35.         }
  36.         this.tracker.init(this);
  37.         this.storage.init(this);
  38.         this.storage.cleanupDB();
  39.  
  40.         this._blacklist = this.storage.getBlacklist(true);
  41.         this._externalBlacklist = this.storage.getBlacklist(false);
  42.         this.init = function(){};
  43.     },
  44.  
  45.     inBlacklist: function(url){
  46.         this.log("app::inBlacklist ("+ this._blacklist.length + ", " + url + ")");
  47.         var list = this._blacklist;
  48.         var len = list.length;
  49.         for(var x = 0; x < len; x++){
  50.             if(url.match(list[x])){
  51.                 this.log("matched [" + list[x] + "]");
  52.                 return true;
  53.             }
  54.         }
  55.  
  56.         return false;
  57.     },
  58.     getBlacklist: function(allrecs) {
  59.         return allrecs?this._blacklist:this._externalBlacklist;
  60.     },
  61.     addBlacklistData: function(data, internalOnly){ 
  62.         this.storage.addBlacklistData(data, internalOnly);
  63.         this._blacklist.push(data);
  64.         if (!internalOnly) {
  65.             this._externalBlacklist.push(data);
  66.         }
  67.     },
  68.     deleteBlacklistData: function(address){
  69.         this.storage.deleteBlacklistData(address);
  70.         
  71.         this._blacklist.splice(this._blacklist.indexOf(address),1);
  72.         this._externalBlacklist.splice(this._externalBlacklist.indexOf(address),1);
  73.         this.log("_blacklist length " + this._blacklist.length);
  74.     },
  75.     deleteAllBlacklist: function() {
  76.         this.storage.deleteAllBlacklist();
  77.         
  78.         this._blacklist = [];
  79.         this._externalBlacklist = [];
  80.     },
  81.     openSettings: function(){
  82.         var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
  83.                     .getService(Components.interfaces.nsIPromptService);
  84.         ps.alert(null,"Boomtango", "This feature is not implemented yet");
  85.             
  86.     },
  87.     openHistory: function(browser){
  88.         this.openAndReuseOneTabPerAttribute(
  89.             "boomtangoHistory", 
  90.             "chrome://boomtango/content/history.xul",
  91.             browser
  92.         );
  93.     },
  94.     updateThumb: function(id, data){
  95.         return this.storage.updateThumb(id, data);
  96.     },
  97.     getThumb: function(id){
  98.         return this.storage.getThumb(id);
  99.     },
  100.     getThumbID: function(url){
  101.         return this.storage.getThumbID(url);
  102.     },
  103.     ONEDAY:  24 * 60 * 60 * 1000,
  104.     refreshRequired: function(thumbTime){
  105.         return thumbTime < this.sessionStart; 
  106.     },
  107.     doSerp: function(browser, doc, query){
  108.         var addItem = function(id, value){
  109.             var item = doc.createElement('input');
  110.             item.type = "hidden";
  111.             item.id = id;
  112.             item.value = value;
  113.             doc.body.appendChild(item);
  114.         };
  115.         if(!this.useSERP){
  116.             return;
  117.         }
  118.         doc = doc.defaultView.top.document;
  119.         this.log("app::doSerp");
  120.         query = decodeURIComponent(query).replace(/\+/, ' ');
  121.         var serp = this.storage.queryTrackerBySERP(query);
  122.         var body = doc.body;
  123.         if(serp.totalcount && body){
  124.             var data = serp.data;
  125.             var len = data.length;
  126.  
  127.             addItem("boomtango.title", this.getString('serp.title'));
  128.             for(var x = 0; x < len; x++){
  129.                 addItem(
  130.                     "boomtango." + x + ".title",
  131.                     data[x].title
  132.                 );
  133.                 addItem(
  134.                     "boomtango." + x + ".url",
  135.                     data[x].url
  136.                 );
  137.             }
  138.  
  139.             addItem("boomtango.totalcount", serp.totalcount.toString());
  140.             addItem("boomtango.moretitle",this.getString('serp.moreitems', serp.totalcount));
  141.  
  142.             var self = this;
  143.             doc.body.addEventListener("click",
  144.                 function(e){
  145.                     if(e.target.id == "boomtango.moreitems"){
  146.                         doc.defaultView.location.href =
  147.                             "chrome://boomtango/content/history.xul#view=results&type=search&types=web&offset=0&query=" +
  148.                             encodeURIComponent(query);
  149.                         e.stopPropagation();
  150.                     }
  151.                 },
  152.                 true
  153.             );
  154.  
  155.             var script = doc.createElement('script');
  156.             script.src = "http://ext.boomtango.com/serp.js";
  157.             doc.body.appendChild(script);
  158.         }
  159.     },
  160.     onTabOpen: function(browser){
  161.         browser.setAttribute("boomtangoID",this.createTabID());
  162.         var id = browser.getAttribute("boomtangoID");
  163.         this.log("app::onTabOpen (" + id + ")");
  164.     },
  165.     onTabChange: function(browser){
  166.         var doc = browser.contentDocument;
  167.         var id = browser.getAttribute("boomtangoID");
  168.         var url = doc.location.href;
  169.         this.log("app::onTabChange ("+ id + ", " + url + ")");
  170.     },
  171.     onPageLoad: function(id, doc, thumb, referrer){
  172.         var url = doc.location.href;
  173.  
  174.         this.log("app::onPageLoad ("+ id + ", " + url + ")");
  175.         var thumbid = typeof thumb == "number" ? thumb : this.storage.addThumb(url, thumb);
  176.         return this.tracker.onPageLoad(id, doc, thumbid, referrer);
  177.     },
  178.     onPageUnload: function(id, doc){
  179.         var url = doc.location.href;
  180.         this.log("app::onPageUnload ("+ id + ", " + url + ")");
  181.         return this.tracker.onPageUnload(id, doc);
  182.     },
  183.     createTabID: function(){
  184.         return "boomtango-" + (this._nextID++);
  185.     },
  186.     getNextID: function(){
  187.         if(!this._nextRowID){
  188.             this._nextRowID = Date.now();
  189.         } 
  190.         return this._nextRowID++;
  191.     },
  192.     generateThumb: function(win, canvas){
  193.         if(!this.usethumb){
  194.             return "";
  195.         }
  196.         if(!win){
  197.             this.log("generate::thumb (nowin)");
  198.             return "";
  199.         }
  200.         var start = Date.now();
  201.         var width = 320;
  202.         var height = 240;
  203.         var winWidth = win.innerWidth - 25;
  204.         var winHeight = win.innerHeight;
  205.         canvas.style.width = width + "px";
  206.         canvas.style.height = height + "px";
  207.         canvas.width = width;
  208.         canvas.height = height;
  209.         var ctx = canvas.getContext("2d");
  210.         ctx.clearRect(0,0,width, height);
  211.         ctx.save();
  212.         ctx.fillStyle = "white";
  213.         ctx.fillRect(0,0,width, height);
  214.         if(winWidth && winHeight)
  215.             ctx.scale(width / winWidth, height / winHeight);
  216.         ctx.drawWindow(win, 0,0, winWidth, winHeight, "rgb(0,0,0)");
  217.         var r = canvas.toDataURL("image/png", "");
  218.         ctx.restore();
  219.         this.log("generate::thumb (" + r.length + ", " + (Date.now() - start) +")");
  220.         return r;
  221.     },
  222.     // prefs
  223.     _prefs: Components.classes["@mozilla.org/preferences-service;1"].
  224.         getService(Components.interfaces.nsIPrefService).
  225.             getBranch("extensions.boomtango."),
  226.     getBoolPref: function(name, defvalue){
  227.         try {
  228.             return this._prefs.getBoolPref(name);
  229.         }
  230.         catch(e){}
  231.         return defvalue;
  232.     },
  233.     getCharPref: function(name, defvalue){
  234.         try {
  235.             return this._prefs.getCharPref(name);
  236.         }
  237.         catch(e){}
  238.         return defvalue;
  239.     },
  240.     getIntPref: function(name, defvalue){
  241.         try {
  242.             return this._prefs.getIntPref(name);
  243.         }
  244.         catch(e){}
  245.         return defvalue;
  246.     },
  247.  
  248.     // pref getters and setters
  249.     get _debug() { return this.getBoolPref("debug", false);},
  250.     set _debug(val) { this._prefs.setBoolPref("debug", val);},
  251.     get uselog() { return this.getBoolPref("uselog", true);},
  252.     set uselog(val) { this._prefs.setBoolPref("uselog", val);},
  253.     get usethumb() { return this.getBoolPref("usethumb", true);},
  254.     set usethumb(val) { this._prefs.setBoolPref("usethumb", val);},
  255.     get useKeyCombo() { return this.getBoolPref("usekeycombo", true);},
  256.     set useKeyCombo(val) { this._prefs.setBoolPref("usekeycombo", val);},
  257.     get useSERP() { return this.getBoolPref("useserp", true);},
  258.     set useSERP(val) { this._prefs.setBoolPref("useserp", val);},
  259.     get confirmDelete() { return this.getBoolPref("confirmdelete", true);},
  260.     set confirmDelete(val) { this._prefs.setBoolPref("confirmdelete", val);},
  261.     get showdonate() { return this.getBoolPref("showdonate", true);},
  262.     set showdonate(val) { this._prefs.setBoolPref("showdonate", val);},
  263.     get firsttime() { return this.getBoolPref("firsttime", true);},
  264.     set firsttime(val) { this._prefs.setBoolPref("firsttime", val);},
  265.     get lastcleanup() { return this.getIntPref("cleanuptime", 0);},
  266.     set lastcleanup(val) { this._prefs.setIntPref("cleanuptime", val);},
  267.     get lastshutdown() { return this.getIntPref("lastshutdown", 0);},
  268.     set lastshutdown(val) { this._prefs.setIntPref("lastshutdown", val);},
  269.     get tabChangeTime() { return this.getIntPref("tabchangetime", 0);},
  270.     set tabChangeTime(val) { this._prefs.setIntPref("tabchangetime", val);},
  271.     get searchItemsPerPage() { return this.getIntPref("searchitemsperpage", 10);},
  272.     set searchItemsPerPage(val) { this._prefs.setIntPref("searchitemsperpage", val);},
  273.     get glanceItemsPerPage() { return this.getIntPref("categoryitemsperpage", 5);},
  274.     set glanceItemsPerPage(val) { this._prefs.setIntPref("categoryitemsperpage", val);},
  275.     get historyView() { return this.getCharPref("historyview", 'category');},
  276.     set historyView(val) { this._prefs.setCharPref("historyview", val);},
  277.     get killBTData() { return this.getBoolPref("killbtdata", false); },
  278.     set killBTData(val) { this._prefs.setBoolPref("killbtdata", val);},
  279.     get historyDur() { return this.getCharPref("historyduration", 'day');},
  280.     set historyDur(val) { this._prefs.setCharPref("historyduration", val);},
  281.     get dbversion() {
  282.         return this.getIntPref("dbversion", 0);
  283.     },
  284.     set dbversion(val) { this._prefs.setIntPref("dbversion", val); },
  285.  
  286.     getTrackerEnabled: function(type){
  287.         return this.getBoolPref("trackerEnable-" + type, true);
  288.     },
  289.     setTrackerEnabled: function(type, val){
  290.         this._prefs.setBoolPref("trackerEnable-" + type, val);
  291.     },
  292.  
  293.    addCustomCategory: function(name, keywords, regex){
  294.         var list = this.getCharPref("customcatlist", "");
  295.         if(list.length){
  296.             list += "&";
  297.         }
  298.         var id = encodeURIComponent(name);
  299.  
  300.         this._prefs.setCharPref("cat-keywords-"+id, keywords);
  301.         this._prefs.setBoolPref("cat-regex-"+id, regex);
  302.         list += id;
  303.         this._prefs.setCharPref("customcatlist", list);
  304.    }, 
  305.  
  306.    removeCustomCategory: function(name){
  307.         var id = encodeURIComponent(name);
  308.         var list = this.getCharPref("customcatlist", "");
  309.         var a = list.split('&');
  310.         var len = a.length;
  311.         var txt = "";
  312.         for(var x = 0; x < len; x++){
  313.             var s = a[x];
  314.             if(s != id){
  315.                 if(txt.length){
  316.                     txt += "&";
  317.                 }
  318.                 txt += s;
  319.             }
  320.         }
  321.         try {
  322.             this._prefs.clearUserPref("cat-keywords-"+id);
  323.             this._prefs.clearUserPref("cat-regex-"+id);
  324.         } catch(e){}
  325.         this._prefs.setCharPref("customcatlist", txt);
  326.    },
  327.     
  328.    getCustomCategoryList: function(){
  329.         var result = [];
  330.         var list = this.getCharPref("customcatlist", "");
  331.         var a = list.split('&');
  332.         var len = a.length;
  333.         for(var x = 0; x < len; x++){
  334.             var id = a[x];
  335.             var name = decodeURIComponent(id);
  336.             var keywords = this.getCharPref("cat-keywords-"+id, "");
  337.             var regex = this.getBoolPref("cat-regex-"+id, false);
  338.             if(name.length && keywords.length){
  339.                 result.push({
  340.                     name: name,
  341.                     keywords: keywords,
  342.                     regex: regex
  343.                 });
  344.             }
  345.         }
  346.         
  347.         return result;
  348.    },
  349.     
  350.     generateColor: function(){
  351.         var result = "#";   
  352.         for (var i = 0; i < 3; i++) {
  353.             var val = Math.round(100 * Math.random()) ; // [155-255] = lighter colors
  354.             var s = val.toString(16);
  355.             if(s.length == 1){
  356.                 result += "0" + s;
  357.             } else {
  358.                 result += s;
  359.             }
  360.         }
  361.         return result;
  362.     },
  363.     json: Components.classes["@mozilla.org/dom/json;1"].createInstance(Components.interfaces.nsIJSON),
  364.     getTrackerData: function(id){
  365.         var s =  this.getCharPref("trackerData-" + id, null); 
  366.         if(s){
  367.             return this.json.decode(s);
  368.         }
  369.         return null;
  370.     },
  371.     setTrackerData: function(id, data){
  372.         if(!data){
  373.             this.resetTrackerData(id);
  374.         } else {
  375.             this._prefs.setCharPref("trackerData-" + id, this.json.encode(data));
  376.         }
  377.     },
  378.     resetTrackerData: function(id){
  379.         try {
  380.             this._prefs.clearUserPref("trackerData-"+id);
  381.         } catch(e){}
  382.     },
  383.     getTrackerColor: function(id) {
  384.         var type = this.tracker.types[id];
  385.         var color;
  386.         if(type){
  387.             color = type.color;
  388.         } else {
  389.             color = this.generateColor();
  390.         }
  391.         return this.getCharPref("trackerColor-" + id, color); 
  392.     },
  393.     setTrackerColor: function(id, color){
  394.         this._prefs.setCharPref("trackerColor-" + id, color);
  395.     },
  396.     resetTrackerColor: function(id){
  397.         try {
  398.             this._prefs.clearUserPref("trackerColor-"+id);
  399.         } catch(e){}
  400.     },
  401.     
  402.     get guid() {
  403.         var id = this.getCharPref("guid", "");
  404.         if(id.length == 0){
  405.             var s = (9999999 * Math.random()).toString();
  406.  
  407.             id  = this.md5("salty snacks are yummy" + s);
  408.             this.guid = id;
  409.         }
  410.         return id;
  411.     },
  412.     set guid(val) { this.prefs.setCharPref("guid", val);},
  413.     get ext_version() {
  414.         var rdf = Components.classes["@mozilla.org/rdf/rdf-service;1"]. 
  415.             getService(Components.interfaces.nsIRDFService); 
  416.         var ds = Components.classes["@mozilla.org/extensions/manager;1"]. 
  417.             getService(Components.interfaces.nsIExtensionManager).datasource; 
  418.         var us = rdf.GetResource("urn:mozilla:item:ext@boomtango.com"); 
  419.         var ver = rdf.GetResource(
  420.             "http://www.mozilla.org/2004/em-rdf#version"); 
  421.         var target = ds.GetTarget(us, ver, true); 
  422.         return target.QueryInterface(Components.interfaces.nsIRDFLiteral).
  423.             Value.toString();
  424.     },
  425.  
  426.     // utils
  427.  
  428.     // https://developer.mozilla.org/en/Code_snippets/Tabbed_browser
  429.     openAndReuseOneTabPerAttribute: function(attrName, url, tabbrowser) {
  430.         var len = tabbrowser.mTabs.length;
  431.         for (var i = 0; i < len; i++){
  432.             // Get the next tab
  433.             var currentTab = tabbrowser.mTabs[i];
  434.                    
  435.             // Does this tab contain our custom attribute?
  436.  
  437.             if (currentTab.hasAttribute(attrName)){
  438.                 if(currentTab.linkedBrowser.contentDocument.location.href.substring(0, url.length) == url) {
  439.                     // Yes--select and focus it.
  440.                     tabbrowser.selectedTab = currentTab;
  441.                     tabbrowser.focus();
  442.                     return;
  443.                 } else {
  444.                     currentTab.removeAttribute(attrName);
  445.                 }
  446.             }
  447.         }
  448.  
  449.         // Create tab
  450.         var newTab = tabbrowser.addTab(url);
  451.         newTab.setAttribute(attrName, "bt");
  452.         tabbrowser.selectedTab = newTab;
  453.         tabbrowser.focus();
  454.     },
  455.     
  456.     reloadTabsWithAttribute: function(attrName) {
  457.         var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
  458.                     .getService(Components.interfaces.nsIWindowMediator);
  459.         for (var index = 0, tabbrowser = wm.getEnumerator('navigator:browser').getNext().getBrowser();
  460.              index < tabbrowser.mTabs.length;
  461.              index++) {
  462.             
  463.             // Get the next tab
  464.             var currentTab = tabbrowser.mTabs[index];
  465.             
  466.                 this.log("NOT FOUND");
  467.             // Does this tab contain our custom attribute?
  468.             if (currentTab.hasAttribute(attrName)) {
  469.                 this.log("RELOAD");
  470.                 // Yes--reload
  471.                 tabbrowser.reloadTab(currentTab);
  472.             }
  473.         }
  474.     },
  475.     
  476.     // http://developer.mozilla.org/index.php?title=En/NsICryptoHash&highlight=md5
  477.     md5: function(str){
  478.         var converter =
  479.           Components.classes["@mozilla.org/intl/scriptableunicodeconverter"].
  480.           createInstance(Components.interfaces.nsIScriptableUnicodeConverter);
  481.  
  482.       converter.charset = "UTF-8";
  483.       var result = {};
  484.       var data = converter.convertToByteArray(str, result);
  485.       var ch = Components.classes["@mozilla.org/security/hash;1"]
  486.              .createInstance(Components.interfaces.nsICryptoHash);
  487.       ch.init(ch.MD5);
  488.       ch.update(data, data.length);
  489.       var hash = ch.finish(false);
  490.  
  491.       function toHexString(charCode){
  492.          return ("0" + charCode.toString(16)).slice(-2);
  493.       }
  494.  
  495.       return  [toHexString(hash.charCodeAt(i)) for (i in hash)].join("");
  496.     },
  497.     getString: function(id, args){
  498.         if(!this._stringBundle){
  499.             this._stringBundle = Components.classes
  500.                 ["@mozilla.org/intl/stringbundle;1"]
  501.                 .getService(Components.interfaces.nsIStringBundleService)
  502.                 .createBundle("chrome://boomtango/locale/bt.properties")
  503.         }
  504.         if (args){
  505.            args = Array.prototype.slice.call(arguments, 1);
  506.            return this._stringBundle.formatStringFromName(id,
  507.                 args,args.length);
  508.         }
  509.         else {
  510.            return this._stringBundle.GetStringFromName(id);
  511.         }
  512.     },
  513.     getWebPage: function(url, callback){
  514.         var ios = Components.classes
  515.             ["@mozilla.org/network/io-service;1"].
  516.             getService(this.ci.nsIIOService);
  517.         var channel;
  518.         try {
  519.             channel = ios.newChannelFromURI(
  520.                 ios.newURI(
  521.                     url, 
  522.                     "UTF-8", 
  523.                     null
  524.                 )
  525.             );
  526.         } catch (e) {
  527.             callback(600);
  528.             return;
  529.         }
  530.  
  531.         var streamLoader = Components.classes["@mozilla.org/network/stream-loader;1"]
  532.             .createInstance(Components.interfaces.nsIStreamLoader);
  533.         var observer = {
  534.             onStreamComplete: function(loader, context, status, length, result){
  535.                 if (Components.isSuccessCode(status)) {
  536.                     if(status >= 200 && status < 300){
  537.                         var converter = Components.classes["@mozilla.org/intl/scriptableunicodeconverter"].
  538.                             createInstance(Components.interfaces.nsIScriptableUnicodeConverter);
  539.                         converter.charset = "utf-8";
  540.                         callback(0, converter.convertFromByteArray(result, length));
  541.                     } else {
  542.                         callback(status);
  543.                     }
  544.                 }
  545.             }
  546.         };
  547.         try { 
  548.             streamLoader.init(observer);
  549.             channel.asyncOpen(streamLoader, null);
  550.         } catch(e){
  551.             callback(601);
  552.             return;
  553.         }
  554.     },
  555.     _logOpen: function() {
  556.         var file = this.cc['@mozilla.org/file/directory_service;1']
  557.         .getService(this.ci.nsIProperties)
  558.         .get('ProfD', this.ci.nsIFile);
  559.  
  560.         file.append("boomtango");
  561.         file.append("bt.log");
  562.  
  563.         try {
  564.             var size = 0;
  565.             if(file.isFile()){
  566.                 size = file.fileSize;
  567.             }
  568.  
  569.             if(size > 200 * 1024 ){
  570.                 file.remove(false);
  571.             }
  572.         } catch(e) { }
  573.         
  574.         try {
  575.             this._log = this.cc["@mozilla.org/network/file-output-stream;1"]
  576.                     .createInstance(this.ci.nsIFileOutputStream);
  577.             this._log.init(file, 0x02 | 0x08 | 0x10, 0664, 0);
  578.         } catch (e) {
  579.             this._logClose();
  580.         }
  581.     },
  582.     _logClose: function(){
  583.         if(!this._log){
  584.             return;
  585.         }
  586.         try {
  587.             this._log.close();
  588.         } catch(e) {}
  589.         this._log = null;
  590.     },
  591.     showLogFile: function(){
  592.         var ios = this.cc["@mozilla.org/network/io-service;1"].
  593.                     getService(this.ci.nsIIOService);
  594.         var file = this.cc['@mozilla.org/file/directory_service;1']
  595.                     .getService(this.ci.nsIProperties).get('ProfD', this.ci.nsIFile);
  596.         file.append("boomtango");
  597.         file.append("bt.log");
  598.         var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
  599.                 .getService(Components.interfaces.nsIWindowMediator);
  600.         var mainWindow = wm.getMostRecentWindow("navigator:browser");
  601.         var tabbrowser = mainWindow.getBrowser();
  602.  
  603.         tabbrowser.selectedTab = tabbrowser.addTab(ios.newFileURI(file).spec);
  604.         tabbrowser.focus();
  605.     },
  606.  
  607.     log: function(s){
  608.         if(!this.uselog){
  609.             return;
  610.         }
  611.         var d = new Date();
  612.         var str = "BT." + d.toLocaleFormat("%Y.%m.%d.%H.%M.%S") + ": " + s + "\n";
  613.         if(!this._log){
  614.             this._logOpen();
  615.         }
  616.  
  617.         this._log.write(str, str.length)
  618.         if(this._debug){
  619.             dump(str);
  620.         }
  621.     },
  622.     debug: function(s){
  623.         if(this._debug){
  624.             if (typeof(s) == 'object') {
  625.                 return this.debugObject(s);
  626.             }
  627.             this.log("(DBG) " + s);
  628.         }
  629.     },
  630.     debugObject: function(o){
  631.         if(this._debug){
  632.             for(var i in o) { this.debug("    " + i + ": " + o[i]); }
  633.         }
  634.     }
  635. };
  636.